package net.wofly.common.persistence;

import lombok.extern.slf4j.Slf4j;
import net.wofly.common.condition.Condition;
import net.wofly.common.condition.ConditionRelate;
import net.wofly.common.condition.FilterCondition;
import net.wofly.common.condition.Operation;
import net.wofly.common.util.DateUtil;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;

/**
 * <br/>
 * Copyright (c) 2014 , 北京可思云海科技有限公司<br/>
 * 2014-10-28 下午2:31:17
 * 
 * @author gyj@kscloud.com
 * @version V1.0
 */
@Slf4j
public class JsonDynamicSpecifications {

	/**
	 * 应用jpa组合查询条件（or的条件用括号括起来，and的也是，而这两组条件通过or进行连接）
	 * 
	 * @param c
	 * @param clazz
	 * @return
	 */
	public static <T> Specification<T> builder(final Condition c, final Class<T> clazz) throws Exception {

		return new Specification<T>() {
			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

				Predicate resultPredicate = builder.conjunction();
				if (c != null && c.getFilterCondition() != null && c.getFilterCondition().size() > 0) {
					Collections.sort(c.getFilterCondition(), new Comparator<FilterCondition>() {
						@Override
						public int compare(FilterCondition o1, FilterCondition o2) {
							if (o1.getSortNo() < o2.getSortNo()) {
								return -1;
							} else if (o1.getSortNo() == o2.getSortNo()) {
								return 0;
							} else {
								return 1;
							}
						}
					});
				} else {
					return resultPredicate;
				}
				Hashtable<Integer, List<FilterCondition>> map = new Hashtable<Integer, List<FilterCondition>>();
				Iterator<FilterCondition> iter = c.getFilterCondition().iterator();
				while (iter.hasNext()) {
					FilterCondition fc = iter.next();
					if(fc.getGroup()==null){
						fc.setGroup(0);
					}
					List<FilterCondition> list = map.get(fc.getGroup());
					if(list==null){
						list = new LinkedList<FilterCondition>();
						map.put(fc.getGroup(), list);
					}
					list.add(fc);
				}
				for(int i=0;i<=10;i++){
					List<FilterCondition> subList =  map.get(i);
					if(subList!=null&&subList.size()>0){
						int flag = 0;
						Iterator<FilterCondition> subIterator = subList.iterator();
						Predicate subPredicate = builder.conjunction();
						while (subIterator.hasNext()) {
							Predicate tempPredicate = builder.conjunction();
							FilterCondition filterCondition = subIterator.next();
							String[] names = StringUtils.split(filterCondition.getAttrName(), ".");
							Class curClass = clazz;
							Path expression = root.get(names[0]);
							for (int j = 1; j < names.length; j++) {
								expression = expression.get(names[j]);
							}
							try {
								if (names.length > 1) {
									curClass = getCurrentClass(names, curClass);
								}
								tempPredicate = buildFilterConditon(filterCondition, expression, curClass, builder);
							} catch (Exception e) {
								log.error("查询字段" + filterCondition.getAttrName() + "语法错误：" + e.getMessage());
							}
							
							if (flag != 0) {
								if (ConditionRelate.or.toString().equalsIgnoreCase(filterCondition.getConditionRelate())) {
									subPredicate = builder.or(subPredicate, tempPredicate);
								} else {
									subPredicate = builder.and(subPredicate, tempPredicate);
								}
							} else {
								subPredicate = tempPredicate;
							}
							flag++;
						}
						if(i==0){
							resultPredicate = subPredicate;
						}else{
							if("or".equalsIgnoreCase(subList.get(0).getGroupRelate())){
								resultPredicate = builder.or(resultPredicate,subPredicate);
							}else{
								resultPredicate = builder.and(resultPredicate,subPredicate);
							}
						}
					}
					
				}
				/*int flag = 0;
				while (iter.hasNext()) {
					Predicate subPredicate = builder.conjunction();
					FilterCondition filterCondition = iter.next();
					String[] names = StringUtils.split(filterCondition.getAttrName(), ".");
					Class curClass = clazz;
					Path expression = root.get(names[0]);
					for (int j = 1; j < names.length; j++) {
						expression = expression.get(names[j]);
					}
					try {
						if (names.length > 1) {
							curClass = getCurrentClass(names, curClass);
						}
						subPredicate = buildFilterConditon(filterCondition, expression, curClass, builder);
					} catch (Exception e) {
						log.error("查询字段" + filterCondition.getAttrName() + "语法错误：" + e.getMessage());
					}
					if (flag != 0) {
						if (ConditionRelate.or.toString().equalsIgnoreCase(filterCondition.getConditionRelate())) {
							resultPredicate = builder.or(resultPredicate, subPredicate);
						} else {
							resultPredicate = builder.and(resultPredicate, subPredicate);
						}
					} else {
						resultPredicate = subPredicate;
					}

					flag++;
				}*/
				return resultPredicate;
			}

			private Class getCurrentClass(String[] names, Class clazz) throws Exception {
				Field field = clazz.getDeclaredField(names[0]);
				Class res = field.getType();
				if (names.length == 2) {
					return res;
				} else {
					return getCurrentClass(Arrays.copyOfRange(names, 1, names.length), res);
				}
			}

			private Predicate buildFilterConditon(final FilterCondition fc, final Path expression, Class<T> clazz, final CriteriaBuilder builder) throws Exception {
				Predicate predicate = builder.conjunction();
				String filedName = fc.getAttrName();
				int pos = filedName.lastIndexOf(".");
				if (pos > 0) {
					filedName = filedName.substring(pos + 1);
				}
				Field field = clazz.getDeclaredField(filedName);
				Class fieldClass = field.getType();

				if (ClassUtils.isPrimitiveOrWrapper(fieldClass)) {
					switch (Operation.valueOf(fc.getOperation())) {
					case neq:
						predicate = builder.notEqual(expression, fc.getVal());
						break;
					case in:
						predicate = builder.gt(expression, NumberUtils.createNumber(fc.getStartVal()));
						predicate = builder.and(builder.lt(expression, NumberUtils.createNumber(fc.getEndVal())));
						break;
					case inr:
						predicate = builder.gt(expression, NumberUtils.createNumber(fc.getStartVal()));
						predicate = builder.and(builder.le(expression, NumberUtils.createNumber(fc.getEndVal())));
						break;
					case linr:
						predicate = builder.ge(expression, NumberUtils.createNumber(fc.getStartVal()));
						predicate = builder.and(builder.le(expression, NumberUtils.createNumber(fc.getEndVal())));
						break;
					case lin:
						predicate = builder.ge(expression, NumberUtils.createNumber(fc.getStartVal()));
						predicate = builder.and(builder.lt(expression, NumberUtils.createNumber(fc.getEndVal())));
						break;
					case gt:
						predicate = builder.gt(expression, NumberUtils.createNumber(fc.getVal()));
						break;
					case ge:
						predicate = builder.ge(expression, NumberUtils.createNumber(fc.getVal()));
						break;
					case lt:
						predicate = builder.lt(expression, NumberUtils.createNumber(fc.getVal()));
						break;
					case le:
						predicate = builder.le(expression, NumberUtils.createNumber(fc.getVal()));
						break;
					case like:
						throw new Exception("数字类型字段不能相似操作!");
					case eq:
						predicate = builder.equal(expression, fc.getVal());
						break;
					}
				} else if (fieldClass.getName().endsWith("Date")) {
					//日期处理
					switch (Operation.valueOf(String.valueOf(fc.getOperation()))) {
					case neq:
						predicate = builder.notEqual(expression, DateUtil.parseDate(fc.getVal()));
						break;
					case in:
						predicate = builder.greaterThan(expression, (Comparable) DateUtil.parseDate(fc.getStartVal()));
						predicate = builder.and(builder.lessThan(expression, (Comparable) DateUtil.parseDate(fc.getEndVal())));
						break;
					case inr:
						predicate = builder.greaterThan(expression, (Comparable) DateUtil.parseDate(fc.getStartVal()));
						predicate = builder.and(builder.lessThanOrEqualTo(expression, (Comparable) DateUtil.parseDate(fc.getEndVal())));
						break;
					case linr:
						predicate = builder.greaterThanOrEqualTo(expression, (Comparable) DateUtil.parseDate(fc.getStartVal()));
						predicate = builder.and(builder.lessThanOrEqualTo(expression, (Comparable) DateUtil.parseDate(fc.getEndVal())));
						break;
					case lin:
						predicate = builder.greaterThan(expression, (Comparable) DateUtil.parseDate(fc.getStartVal()));
						predicate = builder.and(builder.lessThan(expression, (Comparable) DateUtil.parseDate(fc.getEndVal())));
						break;
					case gt:
						predicate = builder.greaterThan(expression, (Comparable) DateUtil.parseDate(fc.getVal()));
						break;
					case ge:
						predicate = builder.greaterThanOrEqualTo(expression, (Comparable) DateUtil.parseDate(fc.getVal()));
						break;
					case lt:
						predicate = builder.lessThan(expression, (Comparable) DateUtil.parseDate(fc.getVal()));
						break;
					case le:
						predicate = builder.lessThanOrEqualTo(expression, (Comparable) DateUtil.parseDate(fc.getVal()));
						break;
					case like:
						throw new Exception("日期类型字段不能相似操作!");
					case eq:
						predicate = builder.equal(expression, fc.getVal());
						break;
					}
				} else {
					switch (Operation.valueOf(String.valueOf(fc.getOperation()))) {
					case neq:
						predicate = builder.notEqual(expression, fc.getVal());
						break;
					case in:
						//predicate = builder.greaterThan(expression, (Comparable) fc.getStartVal());
						//predicate = builder.and(builder.lessThan(expression, (Comparable) fc.getEndVal()));
						CriteriaBuilder.In in  =  builder.in(expression);
						in.value(fc.getInValueList());
						predicate = (Predicate)in;
						break;
					case inr:
						predicate = builder.greaterThan(expression, (Comparable) fc.getStartVal());
						predicate = builder.and(builder.lessThanOrEqualTo(expression, (Comparable) fc.getEndVal()));
						break;
					case linr:
						predicate = builder.greaterThanOrEqualTo(expression, (Comparable) fc.getStartVal());
						predicate = builder.and(builder.lessThanOrEqualTo(expression, (Comparable) fc.getEndVal()));
						break;
					case lin:
						predicate = builder.greaterThan(expression, (Comparable) fc.getStartVal());
						predicate = builder.and(builder.lessThan(expression, (Comparable) fc.getEndVal()));
						break;
					case gt:
						predicate = builder.greaterThan(expression, (Comparable) fc.getVal());
						break;
					case ge:
						predicate = builder.greaterThanOrEqualTo(expression, (Comparable) fc.getVal());
						break;
					case lt:
						predicate = builder.lessThan(expression, (Comparable) fc.getVal());
						break;
					case le:
						predicate = builder.lessThanOrEqualTo(expression, (Comparable) fc.getVal());
						break;
					case like:
						predicate = builder.like(expression, "%" + fc.getVal() + "%");
						break;
					case llike:
						predicate = builder.like(expression, "%" + fc.getVal());
						break;
					case rlike:
						predicate = builder.like(expression, fc.getVal() + "%");
						break;
					case eq:
						predicate = builder.equal(expression, fc.getVal());
						break;
					}
				}
				return predicate;
			}
		};
	}

	static class SearchFilter {

		public String fieldName;
		public Object value;
		public String operator;

		private SearchFilter(String fieldName, String operator, Object value) {
			this.fieldName = fieldName;
			this.value = value;
			this.operator = operator;
		}

		public static SearchFilter newInstance(String fieldName, String operator, Object value) {
			return new SearchFilter(fieldName, operator, value);
		}
	}

}
